/*
 -- IOTA Crypto Core
 --
 -- 2018 by Thomas Pototschnig <microengineer18@gmail.com>
 -- discord: pmaxuw#8292
 -- https://gitlab.com/iccfpga-rv
 --
 -- Permission is hereby granted, free of charge, to any person obtaining
 -- a copy of this software and associated documentation files (the
 -- "Software"), to deal in the Software without restriction, including
 -- without limitation the rights to use, copy, modify, merge, publish,
 -- distribute, sublicense, and/or sell copies of the Software, and to
 -- permit persons to whom the Software is furnished to do so, subject to
 -- the following conditions:
 --
 -- The above copyright notice and this permission notice shall be
 -- included in all copies or substantial portions of the Software.
 --
 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 -- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 -- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 -- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR
 */

#include <unistd.h>
#include <ArduinoJson.h>

#include "api/API.h"

#include "iota/transaction.h"
#include "iota/transfers.h"
#include "iota/bundle.h"

#include "utils.h"

#include "qspi/qspi.h"
#include "base64.h"

namespace API {

	int apiWriteFlashPage() {
		if (
			!exists<uint32_t>(json, "page") ||
			!exists<const char*>(json, "auth") ||
			!exists<const char*>(json, "data")
		) {
			return apiError(SBIError::MISSING_OR_INVALID);
		}

		uint32_t page = json["page"];
		if (page >= (int) SPIFlash::FLASH_PAGE_COUNT) {
			return apiError(SBIError::ERROR);
		}

		const char* auth = json["auth"];
		uint8_t authBytes[48]={0};
		if (!validateHex(auth, 96))
			return apiError(SBIError::INVALID_AUTH);

		if (!hexToBytes(auth, authBytes, sizeof(authBytes))) {
			return SBIError::INVALID_AUTH;
		}


		const char* data = json["data"];
		if (!validateBase64(data, SPIFlash::FLASH_PAGE_LENGTH_BASE64, false)) {
			return apiError(SBIError::INVALID_BASE64_DATA);
		}


		memset(&buffer.scratch[0], 0, SPIFlash::FLASH_PAGE_LENGTH);
		Base64::init();
		uint32_t decoded = Base64::base64_decode(
				data,
				(unsigned char*) &buffer.scratch[0],
				SPIFlash::FLASH_PAGE_LENGTH_BASE64, SPIFlash::FLASH_PAGE_LENGTH + 2);

		if (decoded != SPIFlash::FLASH_PAGE_LENGTH) {
			return apiError(SBIError::INVALID_BASE64_DATA);
		}


		// SBI only uses data for validation of auth - it expects data to be in scratch
		SBIResponse* resp = SBI::writeFlash(page, authBytes);
		if (resp->isError())
			return apiError(resp->code);

		json.clear();

		return API_SUCCESS;
	}

	int apiReadFlashPage() {
		if (
			!exists<uint32_t>(json, "page")
		) {
			return apiError(SBIError::MISSING_OR_INVALID);
		}

		uint32_t page = json["page"];
		if (page >= (int) SPIFlash::FLASH_PAGE_COUNT) {
			return apiError(SBIError::ERROR);
		}

		SBIResponse* resp = SBI::readFlash(page);
		if (resp->isError())
			return apiError(resp->code);

		// use buffer
		memset(&buffer.scratch[SPIFlash::FLASH_PAGE_LENGTH], 0, SPIFlash::FLASH_PAGE_LENGTH_BASE64);
		uint32_t size = Base64::base64_encode(
				(const unsigned char*) &buffer.scratch[0],
				(unsigned char*) 	   &buffer.scratch[SPIFlash::FLASH_PAGE_LENGTH],
				SPIFlash::FLASH_PAGE_LENGTH, SPIFlash::FLASH_PAGE_LENGTH_BASE64);
		if (!size) {
			return apiError(SBIError::ERROR);
		}
		buffer.scratch[SPIFlash::FLASH_PAGE_LENGTH_BASE64+size] = 0; // 0-terminator
		// length-check not needed because fixed length

		json.clear();

		json["page"] = page;
		json["data"] = (const char*) &buffer.scratch[SPIFlash::FLASH_PAGE_LENGTH];

		return API_SUCCESS;
	}

	bool registerAPIFlash() {
		return
				registerAPICall("writeFlashPage", &apiWriteFlashPage) &&
				registerAPICall("readFlashPage", &apiReadFlashPage);
	}


}


